/*
 * Encryption.h
 *
 *  Created on: 2013. 10. 8.
 *      Author: jjaehuny
 */

#ifndef PROJECTZ_ENCRYPTION_H_
#define PROJECTZ_ENCRYPTION_H_

#include <Network/Packet.h>
#include "protocol.h"

#include <algorithm>    // std::find
#include <vector>

#include "NUtil/Single.h"

#include "Config.h"

namespace ProjectZ {

const int s_count = sizeof(NOTENCODECMDS) / sizeof(int);
const std::vector<int> s_cmds(NOTENCODECMDS, NOTENCODECMDS+s_count);

class Encryption : public NUtil::Single<Encryption>
{
public:
	Encryption() : _key1(0), _key2(0), _key3(0), _key4(0) {}
	~Encryption() {}

public:
	void MakeKey()
	{
		uint16_t array[2] = {0,};
		array[0] = 0x5555;
		array[1] = 0x5555;
		_key1 = * ( (int *)(& array[0]));
		_key2 = * ( (int *)(& array[0]));
		_key3 = * ( (int *)(& array[0]));
		_key4 = * ( (int *)(& array[0]));

		_key1 |= 0x1234;
		_key2 |= 0x5678;
		_key3 |= 0x8765;
		_key4 |= 0x4321;
	}

	int GetKey() { return _key1; }
	int GetKey1() { return _key2; }
	int GetKey2() { return _key3; }
	int GetKey3() { return _key4; }

	void Decrypt_First(::Network::Packet * pPacket)
	{
		char public_key[] = "ZenoniaOnline";
		int public_key_len = strlen(public_key);

		if( public_key_len >= 17 )
			public_key_len = 16;

//		if (::ProjectZ::Config::Instance().GetResourceDatabase()._enumNational == CH)
//		{
//			unsigned char * pArray = reinterpret_cast<unsigned char *>(pPacket->GetPtr()+9);
//
//			int k = 0;
//			for (int i = 0; i < pPacket->GetEndLen()-9; ++i)
//			{
//				pArray[i] = pArray[i] ^ public_key[k];
//				if( ++k >= public_key_len ) { k = 0; }
//			}
//		}
//		else
		{
			unsigned char * pArray = reinterpret_cast<unsigned char *>(pPacket->GetPtr()+6);

			int k = 0;
			for (int i = 0; i < pPacket->GetEndLen()-6; ++i)
			{
				pArray[i] = pArray[i] ^ public_key[k];
				if( ++k >= public_key_len ) { k = 0; }
			}
		}
	}
	void Encrypt_First(unsigned char * social_id, int len, ::Network::Packet * pPacket)
	{
		if( len >= 17 )
			len = 16;


		unsigned char * pArray = reinterpret_cast<unsigned char *>(pPacket->GetBasePtr()+6);

		int k = 0;
		for (int i = 0; i < pPacket->GetBaseLen()-6; ++i)
		{
			pArray[i] = pArray[i] ^ social_id[k];
			if( ++k >= len ) { k = 0; }
		}
	}

	void Encrypt(::Network::Packet * pPacket)
	{
		unsigned short cmd = 0;
		cmd = * ( (short *)(pPacket->GetBasePtr()+4) );

		if( std::find(s_cmds.begin(), s_cmds.end(), cmd) != s_cmds.end() )
			return;

		int keys[4];
		keys[0] = GetKey();
		keys[1] = GetKey1();
		keys[2] = GetKey2();
		keys[3] = GetKey3();

		unsigned char * public_keys = reinterpret_cast<unsigned char *>(& keys);
		int public_keys_len = 16;

		unsigned char * pArray = reinterpret_cast<unsigned char *>(pPacket->GetBasePtr()+6);
		int k = 0;
		for (int i = 0; i < pPacket->GetBaseLen()-6; ++i)
		{
			pArray[i] = pArray[i] ^ public_keys[k];
			if( ++k >= public_keys_len ) { k = 0; }
		}

//		int key = GetKey();
//		unsigned char * public_key = reinterpret_cast<unsigned char *>(& key);
//		int public_key_len = 4;
//
//		unsigned char * pArray = reinterpret_cast<unsigned char *>(pPacket->GetBasePtr()+6);
//		int k = 0;
//		for (int i = 0; i < pPacket->GetBaseLen()-6; ++i)
//		{
//			pArray[i] = pArray[i] ^ public_key[k];
//			if( ++k >= public_key_len ) { k = 0; }
//		}
	}
	void Decrypt(::Network::Packet * pPacket)
	{
		unsigned short cmd = 0;
		cmd = * ( (short *)(pPacket->GetPtr()+4) );

		if( std::find(s_cmds.begin(), s_cmds.end(), cmd) != s_cmds.end() )
			return;

		int keys[4];
		keys[0] = GetKey();
		keys[1] = GetKey1();
		keys[2] = GetKey2();
		keys[3] = GetKey3();

		unsigned char * public_keys = reinterpret_cast<unsigned char *>(& keys);
		int public_keys_len = 16;

//		if (::ProjectZ::Config::Instance().GetResourceDatabase()._enumNational == CH)
//		{
//			unsigned char * pArray = reinterpret_cast<unsigned char *>(pPacket->GetPtr()+9);
//			int k = 0;
//			for (int i = 0; i < pPacket->GetEndLen()-9; ++i)
//			{
//				pArray[i] = pArray[i] ^ public_keys[k];
//				if( ++k >= public_keys_len ) { k = 0; }
//			}
//		}
//		else
		{
			unsigned char * pArray = reinterpret_cast<unsigned char *>(pPacket->GetPtr()+6);
			int k = 0;
			for (int i = 0; i < pPacket->GetEndLen()-6; ++i)
			{
				pArray[i] = pArray[i] ^ public_keys[k];
				if( ++k >= public_keys_len ) { k = 0; }
			}
		}

//		int key = GetKey();
//		unsigned char * public_key = reinterpret_cast<unsigned char *>(& key);
//		int public_key_len = 4;
//
//		unsigned char * pArray = reinterpret_cast<unsigned char *>(pPacket->GetPtr()+6);
//		int k = 0;
//		for (int i = 0; i < pPacket->GetEndLen()-6; ++i)
//		{
//			pArray[i] = pArray[i] ^ public_key[k];
//			if( ++k >= public_key_len ) { k = 0; }
//		}
	}

private:
	int _key1;
	int _key2;
	int _key3;
	int _key4;
};

} /*ProjectZ*/

#endif /* PROJECTZ_ENCRYPTION_H_ */

